
package models.spider.request;


import java.net.MalformedURLException;
import java.net.URL;
//import java.util.Random;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;

import play.Logger;

/**
 * NOTE :::
 * http://www.playframework.org/documentation/1.2.5/libs#ParsingXMLusingXPath
 * 
 * @author laurent@opprecht.info
 */
public class Request {



    public class Body {

        protected byte[]   raw  = null;
        protected String   text = null;
        protected Document xml  = null;
        protected Document html = null;

        protected Body(final byte[] raw) {
            this.raw = raw;
        }

        protected Body() {
            this.raw = null;
        }

        public String asText() {
            if (raw == null) {
                return null;
            }
            if (text != null) {
                return text;
            }
/*            String charset = getCharset();
            try {
                if (charset != null) {
                    text = new String(raw, charset);
                } else {
                    text = new String(raw);
                }
            } catch (java.io.UnsupportedEncodingException e) {
            }*/
            text = new String(raw);
            return text;
        }

        public Document asXml() {
            if (raw == null) {
                return null;
            }
            if (xml != null) {
                return xml;
            }
            try {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                javax.xml.parsers.DocumentBuilder builder = factory.newDocumentBuilder();
                Document result = builder.parse(url.toString());
                xml = result;
                return result;
            } catch (Exception ex) {
                Logger.error("asXml", ex);
            }
            return null;
        }

        public Document asHtml() {
            if (raw == null) {
                return null;
            }
            if (html != null) {
                return html;
            }
            try {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                factory.setValidating(false);
                factory.setNamespaceAware(true);
                factory.setIgnoringComments(false);
                factory.setIgnoringElementContentWhitespace(false);
                factory.setExpandEntityReferences(false);
                javax.xml.parsers.DocumentBuilder builder = factory.newDocumentBuilder();
                Document result = builder.parse(url.toString());
                html = result;
                return result;
            } catch (Exception ex) {
                Logger.error("asHtml", ex);
            }
            return null;

        }

    }

    /**
     * @param url
     * @return
     * @deprecated
     */
    public static Request get(final String url) {
        if (StringUtils.isEmpty(url)) {
            return Request.EMPTY;
        } else {

            try {
                return new Request(new URL(url));
            } catch (MalformedURLException e) {
                Logger.error("invalid url " + url, e);
                return Request.EMPTY;
            }
        }
    }

    public static final Request EMPTY           = new Request(null);
    protected URL               url             = null;
    protected Header[]          headers         = new Header[] {};
    protected int               responseCode    = 0;
    protected String            contentEncoding = "";
    protected ContentType       contentType     = null;
    protected int               contentLength   = 0;
    protected Request.Body      body            = null;
    protected HttpClient        client          = null;

    /**
     * @param url
     * @deprecated
     */
    public Request(final URL url) {
        this.url = url;
    }
    
    public Request(final URL url,HttpClient client) {
        this.url = url;
        this.client=client;
    }

    public String getUrl() {
        return url.toString();
    }

    public boolean isLoaded() {
        return body != null;
    }

    public boolean isValid() {
        load();
        return 200 <= responseCode && responseCode <= 299;
    }

    public int getResponseCode() {
        load();
        return responseCode;
    }

    public boolean isHtml() {
        load();
        String mime = getMimeType();
        if (mime == null) {
            return false;
        }
        return mime.toLowerCase().indexOf("html") >= 0;
    }

    public boolean isXml() {
        load();
        String mime = getMimeType();
        if (mime == null) {
            return false;
        }
        return mime.toLowerCase().indexOf("xml") >= 0;
    }

    public boolean isJson() {
        load();
        String mime = getMimeType();
        if (mime == null) {
            return false;
        }
        return mime.toLowerCase().indexOf("json") >= 0;
    }

    public String getContentType() {
        load();
        return (contentType == null) ? "" : contentType.toString();
    }

    public Request.Body body() {
        load();
        return body;
    }

    public String getCharset() {
        load();
        return (contentType == null) ? "" : contentType.toString();
    }

    public String getMimeType() {
        load();
        String contentType = getContentType();

        if (StringUtils.isEmpty(contentType)) {
            return null;
        }

        String[] parts = contentType.split(";");
        if (parts.length == 0) {
            return null;
        }
        return parts[0].trim();

    }

    public boolean connect() {
        body = new Request.Body();
        if (url == null) {
            return false;
        }
        HttpContext context = new BasicHttpContext();
        context.setAttribute(CoreProtocolPNames.USER_AGENT, "Spider RE");
        HttpGet httpGet = null;
        try {
            httpGet = new HttpGet(url.toURI());
        } catch (Exception ex) {
            return false;
        }
        try {
        	//Random randomGenerator = new Random();
        	//int randomInt = randomGenerator.nextInt(1000000);
        	//Logger.info("Lauch Request "+randomInt);
            HttpResponse response = client.execute(httpGet,context);
            //Logger.info("End Request "+randomInt);
            headers = response.getAllHeaders();
            responseCode = response.getStatusLine().getStatusCode();
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                byte[] raw = EntityUtils.toByteArray(entity);
                body = new Request.Body(raw);
                contentEncoding = (entity.getContentEncoding() != null) ? entity.getContentEncoding().getValue() : null;
                contentType = ContentType.getOrDefault(entity);
            } else {
                body = new Request.Body();
                contentEncoding = "";
            }
        } catch (Exception ex) {
            if (httpGet != null) {
                httpGet.abort();
            }
            Logger.error("Connection faileds", ex);
            httpGet.releaseConnection();
            return false;
        }

        httpGet.releaseConnection();
        return true;

    }

    /**
     * If resource is not yet loaded call connect to fetch it. Otherwise exit.
     * Used to lazy load the resource if required.
     */
    protected void load() {
        if (isLoaded()) {
            return;
        }
        connect();
    }
}
